package com.wmx.hb.controller;

import cn.hutool.core.util.NumberUtil;
import cn.hutool.core.util.ObjectUtil;
import com.wmx.hb.enums.ResultCode;
import com.wmx.hb.mapper.EmpMapper;
import com.wmx.hb.pojo.Emp;
import com.wmx.hb.pojo.ResultData;
import com.wmx.hb.service.EmpService;
import org.apache.ibatis.cursor.Cursor;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.bind.annotation.*;

import javax.annotation.Resource;
import java.io.Closeable;
import java.io.IOException;
import java.util.*;

/**
 * 员工控制层
 *
 * @author wangMaoXiong
 * @version 1.0
 * @date 2021/7/4 16:45
 */
@RestController
public class EmpController {

    private static Logger logger = LoggerFactory.getLogger(EmpController.class);

    @Resource
    private EmpMapper empMapper;

    @Resource
    private EmpService empService;

    //SqlSessionFactory 是线程安全的.
    @Resource
    private SqlSessionFactory sqlSessionFactory;

    /**
     * http://localhost:8080/emp/getEmp
     * {"empno":1}
     * {"ename":"m"}
     * {"job":"manager"}
     * {"hiredate":"1995-04-14"}
     * 按顺序将实体类 Emp 第一个不为空的属性作为 where 查询条件
     *
     * @param emp
     * @return
     */
    @PostMapping("/emp/getEmp")
    public ResultData<Emp> getEmp(@RequestBody Emp emp) {
        List<Emp> empList = empMapper.getEmp(emp);
        return new ResultData(ResultCode.SUCCESS, empList);
    }

    /**
     * 查询薪资在[start,end]之间的员工，start 为空时，不设下限，end 为空时，不设上限.
     * http://localhost:8080/emp/getEmpBySal
     * http://localhost:8080/emp/getEmpBySal?start=2000
     * http://localhost:8080/emp/getEmpBySal?end=3000
     * http://localhost:8080/emp/getEmpBySal?start=2000&end=3000
     *
     * @param start
     * @param end
     * @return
     */
    @GetMapping("/emp/getEmpBySal")
    public ResultData<Emp> getEmpBySal(Double start, Double end) {
        Map<String, Double> paramMap = new HashMap<>();
        paramMap.put("start", start);
        paramMap.put("end", end);
        List<Emp> empList = empMapper.getEmpBySal(paramMap);
        return new ResultData(ResultCode.SUCCESS, empList);
    }

    /**
     * 批量插入方式 1 —— 底层是单个元素循环插入
     * http://localhost:8080/emp/batchInserts
     *
     * @return
     */
    @GetMapping("/emp/batchInserts")
    public ResultData<Map> batchInserts() {
        List<Emp> empList = this.getDatas();
        empService.batchInserts(empList);
        return new ResultData(ResultCode.SUCCESS, null);
    }

    /**
     * 批量插入方式 2 —— 底层是纯 sql 方式：insert into tableName(k1,k2,k3) values (v1, v2, v3),(v1, v2, v3);
     * http://localhost:8080/emp/batchAdds
     *
     * @return
     */
    @GetMapping("/emp/batchAdds")
    public ResultData<Map> batchAdds() {
        List<Emp> empList = this.getDatas();
        empService.batchAdds(empList);
        return new ResultData(ResultCode.SUCCESS, null);
    }

    /**
     * 批量插入方式 3 —— 底层通过 SqlSession 的批处理，这也是官方提供的示例.
     * http://localhost:8080/emp/batchSaves
     *
     * @return
     */
    @GetMapping("/emp/batchSaves")
    public ResultData<Map> batchSaves() {
        List<Emp> empList = this.getDatas();
        empService.batchSaves(empList);
        return new ResultData(ResultCode.SUCCESS, null);
    }

    /**
     * 批量插入方式 4 —— 底层使用 Oracle Insert All 语法批量插入
     * http://localhost:8080/emp/batchInserts2
     *
     * @return
     */
    @GetMapping("/emp/batchInserts2")
    public ResultData<Map> batchInserts2() {
        List<Emp> empList = this.getDatas();
        empService.batchInserts2(empList);
        return new ResultData(ResultCode.SUCCESS, null);
    }

    private List<Emp> getDatas() {
        int size = 1;
        Random random = new Random();
        List<Emp> datas = new ArrayList<>(size);
        for (int i = 0; i < size; i++) {
            Emp emp = new Emp();
            emp.setEname("张三" + i);
            emp.setJob("Java开发工程师");
            emp.setMgr(7698);
            emp.setHiredate(new Date());
            emp.setSal((10000 + random.nextInt(3000)) * 1.0F);
            if (random.nextInt(10) % 2 == 0) {
                emp.setComm(400F);
            }
            emp.setDeptno(2);
            datas.add(emp);
        }
        return datas;
    }

    /**
     * http://localhost:8080/emp/findAllEmps
     * <p>
     * 1、Transactional 事务注解加持之后，整个方法都是同一个数据库连接，且方法执行完成后才会自动关闭连接。
     * 2、如果没有 Transactional 事务注解加持，则 Mapper 接口方法执行完成后，连接就自动关闭了，当再次遍历游标取数据时就会报错：
     * * java.lang.IllegalStateException:A Cursor is already closed.
     * 3、try(xxx) 中支持定义继承 {@link Closeable} 的 API，在 try 块执行完毕后，自动会调用 close 方法关闭。
     *
     * @return
     */
    @GetMapping("/emp/findAllEmps")
    @Transactional(rollbackFor = Exception.class)
    public ResultData<Emp> findAllEmps() throws IOException {
        try (Cursor<Emp> allEmps = empMapper.findAllEmps()) {
            Iterator<Emp> iterator = allEmps.iterator();
            while (iterator.hasNext()) {
                Emp emp = iterator.next();
                int currentIndex = allEmps.getCurrentIndex();
                System.out.println(currentIndex + "：" + emp);
            }
        }
        return new ResultData(ResultCode.SUCCESS, null);
    }

    /**
     * http://localhost:8080/emp/loadAllEmps
     * <p>
     * 1、上面事务注解的方式是最简单的，手动开关 SqlSession 同样也可以了。
     * 2、try(xxx) 中支持定义继承 {@link Closeable} 的 API，在 try 块执行完毕后，自动会调用 close 方法关闭。
     *
     * @return
     */
    @GetMapping("/emp/loadAllEmps")
    public ResultData<Emp> loadAllEmps() throws IOException {
        // 一个 SqlSession 就代表一个数据库连接
        try (SqlSession sqlSession = sqlSessionFactory.openSession();
             Cursor<Emp> allEmps = sqlSession.getMapper(EmpMapper.class).findAllEmps()) {
            allEmps.forEach(item -> System.out.println(item));
        }
        return new ResultData(ResultCode.SUCCESS, null);
    }

    /**
     * http://localhost:8080/emp/getEmpById?empno=7788
     * 根据主键查询员工信息————一对一关联查询员工所属的部门信息
     *
     * @param empno
     * @return
     */
    @GetMapping("/emp/getEmpById")
    public ResultData<Emp> getEmpById(@RequestParam int empno) {
        Emp emp = empMapper.getEmpById(empno);
        return new ResultData(ResultCode.SUCCESS, emp);
    }

    /**
     * http://localhost:8080/emp/getEmpByName?ename=张
     * 根据姓名查询员工信息————一对一关联查询员工所属的部门信息
     *
     * @param ename
     * @return
     */
    @GetMapping("/emp/getEmpByName")
    public ResultData<Emp> getEmpByName(@RequestParam String ename) {
        List<Emp> empList = empMapper.getEmpByName(ename);
        return new ResultData(ResultCode.SUCCESS, empList);
    }

    /**
     * 根据主键ID集合批量查询
     * http://localhost:8080/emp/loadByIds?deptNo=30&ids=7521,7698,7788
     * http://localhost:8080/emp/loadByIds?ids=7521,7698,7788
     * <pre>
     *      ==> Preparing: select * from emp where deptno =? and empno in ( ? , ? , ? )
     *      ==> Parameters: 30(Integer), 7521(Integer), 7698(Integer), 7788(Integer)
     * </pre>
     * <pre>
     *      ==> Preparing: select * from emp where empno in ( ? , ? , ? )
     *      ==> Parameters: 7521(Integer), 7698(Integer), 7788(Integer)
     * </pre>
     *
     * @param deptNo ：部门编号
     * @param ids    ：员工主键ID，逗号隔开
     * @return
     */
    @GetMapping("/emp/loadByIds")
    public ResultData<Emp> loadByIds(@RequestParam(required = false) Integer deptNo,
                                     @RequestParam String ids) {
        String[] split = ids.split(",");
        List<Integer> idList = new ArrayList<>();
        for (String s : split) {
            if (NumberUtil.isInteger(s)) {
                idList.add(Integer.parseInt(s));
            }
        }
        List<Emp> empList = empMapper.loadByIds(deptNo, idList.toArray(new Integer[0]));
        return new ResultData(ResultCode.SUCCESS, empList);
    }

    /**
     * 通过存储过程(函数)查询全部员工信息
     * http://localhost:8080/emp/proLoadAllEmps
     *
     * @return
     */
    @GetMapping("/emp/proLoadAllEmps")
    public ResultData<Emp> proLoadAllEmps() {
        List<Emp> empList = empMapper.proLoadAllEmps();
        return new ResultData(ResultCode.SUCCESS, empList);
    }

    /**
     * 通过存储过程(函数)查询指定部门下的员工信息
     * http://localhost:8080/emp/proLoadEmpsByDeptno?deptNo=1
     *
     * @return
     */
    @GetMapping("/emp/proLoadEmpsByDeptno")
    public ResultData<Emp> proLoadEmpsByDeptno(@RequestParam(value = "deptNo") int deptNo) {
        List<Emp> empList = empMapper.proLoadEmpsByDeptno(deptNo);
        return new ResultData(ResultCode.SUCCESS, empList);
    }

    /**
     * 通过存储过程(函数)根据员工编号查询员工姓名
     * http://localhost:8080/emp/proLoadEnameByEmpno?empNo=1
     *
     * @return
     */
    @GetMapping("/emp/proLoadEnameByEmpno")
    public ResultData<Emp> proLoadEnameByEmpno(@RequestParam(value = "empNo") int empNo) {
        Map<String, Object> params = new HashMap<>(8);
        params.put("empNo", empNo);
        empMapper.proLoadEnameByEmpno(params);
        return new ResultData(ResultCode.SUCCESS, params);
    }

    /**
     * 通过存储过程(函数)新增员工信息
     * http://localhost:8080/emp/proSaveEmp
     * <p>
     * {"ename":"李元霸","job":"clerk","mgr":7902,"hiredate":"1993-08-08","sal":12000,"comm":0,"deptno":2}
     *
     * @return
     */
    @PostMapping("/emp/proSaveEmp")
    public ResultData<String> proSaveEmp(@RequestBody Emp emp) {
        empMapper.proSaveEmp(emp);
        return new ResultData(ResultCode.SUCCESS);
    }

}
